package org.apache.http.client.fluent;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.ChallengeState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.ssl.SSLInitializationException;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.protocol.BasicHttpContext;

public class Executor
{
  static PoolingClientConnectionManager CONNMGR=new PoolingClientConnectionManager();
  static final DefaultHttpClient CLIENT = new DefaultHttpClient(CONNMGR);
  private final HttpClient httpclient;
  private final BasicHttpContext localContext;
  private final AuthCache authCache;
  private CredentialsProvider credentialsProvider;
  private CookieStore cookieStore;

  public static Executor newInstance()
  {
    return new Executor(CLIENT);
  }

  public static Executor newInstance(HttpClient httpclient) {
    return new Executor(httpclient != null ? httpclient : CLIENT);
  }

  Executor(HttpClient httpclient)
  {
    this.httpclient = httpclient;
    this.localContext = new BasicHttpContext();
    this.authCache = new BasicAuthCache();
  }

  public Executor auth(AuthScope authScope, Credentials creds) {
    if (this.credentialsProvider == null) {
      this.credentialsProvider = new BasicCredentialsProvider();
    }
    this.credentialsProvider.setCredentials(authScope, creds);
    return this;
  }

  public Executor auth(HttpHost host, Credentials creds) {
    AuthScope authScope = host != null ? new AuthScope(host) : AuthScope.ANY;
    return auth(authScope, creds);
  }

  public Executor authPreemptive(HttpHost host) {
    this.authCache.put(host, new BasicScheme(ChallengeState.TARGET));
    return this;
  }

  public Executor authPreemptiveProxy(HttpHost host) {
    this.authCache.put(host, new BasicScheme(ChallengeState.PROXY));
    return this;
  }

  public Executor auth(Credentials cred) {
    return auth(AuthScope.ANY, cred);
  }

  public Executor auth(String username, String password) {
    return auth(new UsernamePasswordCredentials(username, password));
  }

  public Executor auth(String username, String password, String workstation, String domain)
  {
    return auth(new NTCredentials(username, password, workstation, domain));
  }

  public Executor auth(HttpHost host, String username, String password)
  {
    return auth(host, new UsernamePasswordCredentials(username, password));
  }

  public Executor auth(HttpHost host, String username, String password, String workstation, String domain)
  {
    return auth(host, new NTCredentials(username, password, workstation, domain));
  }

  public Executor clearAuth() {
    if (this.credentialsProvider != null) {
      this.credentialsProvider.clear();
    }
    return this;
  }

  public Executor cookieStore(CookieStore cookieStore) {
    this.cookieStore = cookieStore;
    return this;
  }

  public Executor clearCookies() {
    if (this.cookieStore != null) {
      this.cookieStore.clear();
    }
    return this;
  }

  public Response execute(Request request)
    throws ClientProtocolException, IOException
  {
    this.localContext.setAttribute("http.auth.credentials-provider", this.credentialsProvider);
    this.localContext.setAttribute("http.auth.auth-cache", this.authCache);
    this.localContext.setAttribute("http.cookie-store", this.cookieStore);
    HttpRequestBase httprequest = request.getHttpRequest();
    httprequest.reset();
    return new Response(this.httpclient.execute(httprequest, this.localContext));
  }

  public static void registerScheme(Scheme scheme) {
    CONNMGR.getSchemeRegistry().register(scheme);
  }

  public static void unregisterScheme(String name) {
    CONNMGR.getSchemeRegistry().unregister(name);
  }

  static
  {
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    SchemeSocketFactory plain = PlainSocketFactory.getSocketFactory();
    schemeRegistry.register(new Scheme("http", 80, plain));
    SchemeSocketFactory ssl = null;
    try {
      ssl = SSLSocketFactory.getSystemSocketFactory();
    }
    catch (SSLInitializationException ex) {
      try {
        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(null, null, null);
        ssl = new SSLSocketFactory(sslcontext);
      } catch (SecurityException ignore) {
      } catch (KeyManagementException ignore) {
      } catch (NoSuchAlgorithmException ignore) {
      }
    }
    if (ssl != null) {
      schemeRegistry.register(new Scheme("https", 443, ssl));
    }
    CONNMGR = new PoolingClientConnectionManager(schemeRegistry);
    CONNMGR.setDefaultMaxPerRoute(100);
    CONNMGR.setMaxTotal(200);
  }
}